// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "grpc.h"
#include "Ninf.h"
#include "Global.hpp"
#include "GrpcImpl.hpp"
#include "Exception.hpp"
#include "CallException.hpp"


using namespace ninf::client;

static const char *grpc_l_error_message[] = {
    "No error",
    "GRPC client not initialized yet",
    "The function is called more than once",
    "Specified configuration file not found",
    "Specified configuration file Error",
    "GRPC client cannot find any server",
    "GRPC client cannot find the function",
    "Function handle is not valid",
    "Session ID is not valid",
    "RPC invocation refused by the server",
    "Communication with the server failed",
    "The specified session failed",
    "Call has not completed",
    "No calls have completed",
    "Object handle is not valid",
    "GRPC client cannot find the class",
    "Timeout",
    "Session canceled",
    "Internal error detected",
    "Unknown Error code",
    "Highest numerical error code"
};

static int ErrorCodeMapping [] = {
		GRPC_NO_ERROR,             //"NO_ERROR",
		GRPC_FUNCTION_NOT_FOUND,   //"NO_STUB_INFO",
		GRPC_OTHER_ERROR_CODE,     //"INTERNAL_ERROR",
		GRPC_COMMUNICATION_FAILED, //"COMMUNICATION_ERROR",  
		GRPC_RPC_REFUSED,          //"SUBMIT_ERROR",
		GRPC_OTHER_ERROR_CODE,     //"CANCELED"
		GRPC_NONE_COMPLETED,       //"NON_COMPLETED"
		GRPC_INVALID_SESSION_ID,   //"INVALID_SESSION_ID"
		GRPC_LAST_ERROR_CODE,
};

static int errorCodeMapping(int id){
		if (id < 0 || id >= NINF_LAST_ERROR_CODE) 
				return GRPC_OTHER_ERROR_CODE;
		return ErrorCodeMapping[id];
}

/************************* INIT, FINAILIZE ************************/
static bool grpc_initialized;

grpc_error_t grpc_initialize(const char * filename){
		if (grpc_initialized)
				return GRPC_ALREADY_INITIALIZED;
		try {
				Global::global.readConf(filename);
				Global::global.init();
		} catch (Exception e){
				cerr << e;
				return GRPC_OTHER_ERROR_CODE;
		}
		grpc_initialized = true;
		return GRPC_NO_ERROR;
}

grpc_error_t grpc_finalize(void) {
		if (!grpc_initialized)
				return GRPC_NOT_INITIALIZED;
		grpc_initialized = false;
		return errorCodeMapping(NinfFinalize());
}

/* Function handle management API */

grpc_error_t grpc_function_handle_init(
		grpc_function_handle_t * func, 
    const char * server_name, 
    const char * entry_name) {
		if (!grpc_initialized)
				return GRPC_NOT_INITIALIZED;
		if (server_name == NULL || entry_name == NULL) 
				return GRPC_SERVER_NOT_FOUND;
		func->server_name = strdup(server_name);
		func->entry_name = strdup(entry_name);
		return GRPC_NO_ERROR;
}
		
grpc_error_t 
grpc_function_handle_default(
	 	grpc_function_handle_t * handle, 
    const char * entry_name){
		if (!grpc_initialized)
				return GRPC_NOT_INITIALIZED;
		if (entry_name == NULL) 
				return GRPC_SERVER_NOT_FOUND;
		handle->server_name = NULL;
		handle->entry_name = strdup(entry_name);
		return GRPC_NO_ERROR;
}

grpc_error_t 
grpc_function_handle_destruct(
    grpc_function_handle_t * handle){
		if (handle->server_name != NULL)
				free(handle->server_name);
		if (handle->entry_name != NULL)
				free(handle->entry_name);
		return GRPC_NO_ERROR;
}


grpc_error_t 
grpc_call(
    grpc_function_handle_t * handle, ...){
		NinfErrorCode errorCode;
		Session * session;
		va_list ap;

		if (!grpc_initialized)
				return GRPC_NOT_INITIALIZED;

		try {
				fprintf(stderr, "try to create Session\n");
				session = new Session(handle->entry_name, true);
		} catch (CallException e) {
				return errorCodeMapping(e.errorCode);
		}
		
		va_start(ap, handle);
		errorCode = session->invoke(ap);
		va_end(ap);
		if (errorCode != NINF_NO_ERROR)
				return errorCodeMapping(errorCode);
		
		try {
				Global::global.sessionManager.wait(session->id);
		} catch (Exception e) {
				cerr << e;
				return errorCodeMapping(NINF_INTERNAL_ERROR);
		}
		return errorCodeMapping(NINF_NO_ERROR);
}

grpc_error_t 
grpc_call_async(
    grpc_function_handle_t * handle, 
    grpc_sessionid_t * pSessionId,
		...){
		NinfErrorCode errorCode;
		Session * session;
		va_list ap;

		if (!grpc_initialized)
				return GRPC_NOT_INITIALIZED;

		try {
				fprintf(stderr, "try to create Session\n");
				session = new Session(handle->entry_name, true);
		} catch (CallException e) {
				return errorCodeMapping(e.errorCode);
		}
		
		va_start(ap, pSessionId);
		errorCode = session->invoke(ap);
		va_end(ap);
		if (errorCode != NINF_NO_ERROR)
				return errorCodeMapping(errorCode);
		* pSessionId = session->id;
		
		return errorCodeMapping(NINF_NO_ERROR);
}


/* Asynchronous GridRPC wait API */
grpc_error_t 
grpc_wait(grpc_sessionid_t id){
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfWait(id));
}

grpc_error_t 
grpc_wait_all(){
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfWaitAll());
}

grpc_error_t 
grpc_wait_any(grpc_sessionid_t * id){
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfWaitAny(id));
}

grpc_error_t 
grpc_wait_and(grpc_sessionid_t * idList, size_t length) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfWaitAnd(idList, length));
}

grpc_error_t 
grpc_wait_or(grpc_sessionid_t * idList, size_t length, grpc_sessionid_t * id) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfWaitOr(idList, length, id));
}

/* Asynchronous GridRPC control API */
grpc_error_t 
grpc_cancel(grpc_sessionid_t id) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfCancel(id));
}

grpc_error_t 
grpc_cancel_all(void) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfCancelAll());
}


grpc_error_t grpc_probe(grpc_sessionid_t id) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfProbe(id));
}

grpc_error_t grpc_probe_or(grpc_sessionid_t * idList,
													 size_t length, grpc_sessionid_t * id){
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfProbeOr(idList, length, id));
}

/* Error Reporting API */
const char * grpc_error_string(grpc_error_t error) {
		if (error < 0 || error > GRPC_LAST_ERROR_CODE) 
				return "no such error";
		return grpc_l_error_message[error];
}

grpc_error_t  grpc_get_error(grpc_sessionid_t  id) {
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfGetError(id));
}
 
grpc_error_t grpc_get_failed_sessionid(grpc_sessionid_t * idP){
		if (!grpc_initialized) return GRPC_NOT_INITIALIZED;
		return errorCodeMapping(NinfGetFailedSessionID(idP));
}



